import os
import zipfile
import tkinter as tk
from tkinter import filedialog, ttk, messagebox
from tkinter.scrolledtext import ScrolledText
import threading
import time
from pathlib import Path

class FileCompressorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("文件压缩解压工具")
        self.root.geometry("800x600")
        self.root.minsize(600, 400)
        
        # 设置主题颜色
        self.bg_color = "#f0f0f0"
        self.accent_color = "#4a86e8"
        self.root.configure(bg=self.bg_color)
        
        # 创建主框架
        self.main_frame = ttk.Frame(root)
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 创建标签页
        self.tab_control = ttk.Notebook(self.main_frame)
        
        # 创建压缩标签页
        self.compress_tab = ttk.Frame(self.tab_control)
        self.tab_control.add(self.compress_tab, text="压缩")
        
        # 创建解压标签页
        self.extract_tab = ttk.Frame(self.tab_control)
        self.tab_control.add(self.extract_tab, text="解压")
        
        self.tab_control.pack(fill=tk.BOTH, expand=True)
        
        # 设置压缩标签页
        self.setup_compress_tab()
        
        # 设置解压标签页
        self.setup_extract_tab()
        
        # 状态栏
        self.status_bar = ttk.Label(root, text="准备就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        
        # 初始化变量
        self.files_to_compress = []
        self.extract_file = ""
        self.extract_path = ""
        self.compress_running = False
        self.extract_running = False

    def setup_compress_tab(self):
        # 压缩标签页的布局
        frame = ttk.Frame(self.compress_tab, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)
        
        # 文件选择区域
        file_frame = ttk.LabelFrame(frame, text="选择要压缩的文件或文件夹", padding="10")
        file_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        
        # 文件列表
        self.file_listbox = ScrolledText(file_frame, height=10, width=50)
        self.file_listbox.pack(fill=tk.BOTH, expand=True, pady=5)
        
        # 按钮区域
        button_frame = ttk.Frame(file_frame)
        button_frame.pack(fill=tk.X, pady=5)
        
        ttk.Button(button_frame, text="添加文件", command=self.add_files).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="添加文件夹", command=self.add_folder).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="清空列表", command=self.clear_files).pack(side=tk.LEFT, padx=5)
        
        # 输出设置区域
        output_frame = ttk.LabelFrame(frame, text="压缩设置", padding="10")
        output_frame.pack(fill=tk.X, pady=10)
        
        ttk.Label(output_frame, text="输出文件:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
        
        self.output_var = tk.StringVar()
        output_entry = ttk.Entry(output_frame, textvariable=self.output_var, width=50)
        output_entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W+tk.E)
        
        ttk.Button(output_frame, text="浏览...", command=self.select_output_file).grid(row=0, column=2, padx=5, pady=5)
        
        # 压缩级别设置
        ttk.Label(output_frame, text="压缩级别:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=5)
        
        self.compress_level = tk.IntVar(value=6)
        compress_scale = ttk.Scale(output_frame, from_=1, to=9, variable=self.compress_level, orient=tk.HORIZONTAL)
        compress_scale.grid(row=1, column=1, padx=5, pady=5, sticky=tk.W+tk.E)
        
        self.level_label = ttk.Label(output_frame, text="6")
        self.level_label.grid(row=1, column=2, padx=5, pady=5)
        
        compress_scale.bind("<Motion>", self.update_level_label)
        
        # 进度条区域
        progress_frame = ttk.Frame(frame)
        progress_frame.pack(fill=tk.X, pady=10)
        
        self.compress_progress = ttk.Progressbar(progress_frame, orient=tk.HORIZONTAL, length=100, mode='determinate')
        self.compress_progress.pack(fill=tk.X, pady=5)
        
        # 开始压缩按钮
        self.compress_button = ttk.Button(progress_frame, text="开始压缩", command=self.start_compression)
        self.compress_button.pack(pady=5)

    def setup_extract_tab(self):
        # 解压标签页的布局
        frame = ttk.Frame(self.extract_tab, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)
        
        # 文件选择区域
        file_frame = ttk.LabelFrame(frame, text="选择要解压的文件", padding="10")
        file_frame.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Label(file_frame, text="压缩文件:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
        
        self.extract_file_var = tk.StringVar()
        extract_entry = ttk.Entry(file_frame, textvariable=self.extract_file_var, width=50)
        extract_entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W+tk.E)
        
        ttk.Button(file_frame, text="浏览...", command=self.select_extract_file).grid(row=0, column=2, padx=5, pady=5)
        
        # 输出路径设置
        ttk.Label(file_frame, text="解压到:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=5)
        
        self.extract_path_var = tk.StringVar()
        extract_path_entry = ttk.Entry(file_frame, textvariable=self.extract_path_var, width=50)
        extract_path_entry.grid(row=1, column=1, padx=5, pady=5, sticky=tk.W+tk.E)
        
        ttk.Button(file_frame, text="浏览...", command=self.select_extract_path).grid(row=1, column=2, padx=5, pady=5)
        
        # 文件内容显示区域
        content_frame = ttk.LabelFrame(frame, text="压缩文件内容", padding="10")
        content_frame.pack(fill=tk.BOTH, expand=True, pady=10)
        
        self.content_text = ScrolledText(content_frame, height=10, width=50)
        self.content_text.pack(fill=tk.BOTH, expand=True, pady=5)
        
        ttk.Button(content_frame, text="查看内容", command=self.view_zip_content).pack(pady=5)
        
        # 进度条区域
        progress_frame = ttk.Frame(frame)
        progress_frame.pack(fill=tk.X, pady=10)
        
        self.extract_progress = ttk.Progressbar(progress_frame, orient=tk.HORIZONTAL, length=100, mode='determinate')
        self.extract_progress.pack(fill=tk.X, pady=5)
        
        # 开始解压按钮
        self.extract_button = ttk.Button(progress_frame, text="开始解压", command=self.start_extraction)
        self.extract_button.pack(pady=5)

    def add_files(self):
        files = filedialog.askopenfilenames(title="选择文件")
        if files:
            for file in files:
                self.files_to_compress.append(file)
                self.file_listbox.insert(tk.END, file + "\n")

    def add_folder(self):
        folder = filedialog.askdirectory(title="选择文件夹")
        if folder:
            self.files_to_compress.append(folder)
            self.file_listbox.insert(tk.END, folder + "\n")

    def clear_files(self):
        self.files_to_compress = []
        self.file_listbox.delete(1.0, tk.END)

    def select_output_file(self):
        file = filedialog.asksaveasfilename(
            title="保存压缩文件",
            defaultextension=".zip",
            filetypes=[("ZIP 文件", "*.zip"), ("所有文件", "*.*")]
        )
        if file:
            self.output_var.set(file)

    def select_extract_file(self):
        file = filedialog.askopenfilename(
            title="选择压缩文件",
            filetypes=[("ZIP 文件", "*.zip"), ("所有文件", "*.*")]
        )
        if file:
            self.extract_file_var.set(file)
            self.extract_file = file

    def select_extract_path(self):
        path = filedialog.askdirectory(title="选择解压目录")
        if path:
            self.extract_path_var.set(path)
            self.extract_path = path

    def update_level_label(self, event=None):
        self.level_label.config(text=str(self.compress_level.get()))

    def view_zip_content(self):
        extract_file = self.extract_file_var.get()
        if not extract_file:
            messagebox.showerror("错误", "请先选择一个压缩文件")
            return
            
        try:
            with zipfile.ZipFile(extract_file, 'r') as zip_ref:
                self.content_text.delete(1.0, tk.END)
                for file_info in zip_ref.infolist():
                    size_kb = file_info.file_size / 1024
                    compressed_size_kb = file_info.compress_size / 1024
                    ratio = 0 if file_info.file_size == 0 else (1 - file_info.compress_size / file_info.file_size) * 100
                    
                    self.content_text.insert(tk.END, f"{file_info.filename}\n")
                    self.content_text.insert(tk.END, f"  大小: {size_kb:.2f} KB\n")
                    self.content_text.insert(tk.END, f"  压缩后: {compressed_size_kb:.2f} KB\n")
                    self.content_text.insert(tk.END, f"  压缩率: {ratio:.2f}%\n\n")
        except Exception as e:
            messagebox.showerror("错误", f"无法打开压缩文件: {str(e)}")

    def start_compression(self):
        if not self.files_to_compress:
            messagebox.showerror("错误", "请添加要压缩的文件或文件夹")
            return
            
        output_file = self.output_var.get()
        if not output_file:
            messagebox.showerror("错误", "请选择输出文件位置")
            return
            
        if self.compress_running:
            messagebox.showinfo("提示", "压缩任务正在进行中，请等待完成")
            return
            
        self.compress_running = True
        self.compress_button.config(state=tk.DISABLED)
        self.status_bar.config(text="压缩中...")
        
        # 使用线程避免界面卡死
        threading.Thread(target=self.compress_files, daemon=True).start()

    def compress_files(self):
        try:
            level = self.compress_level.get()
            output_file = self.output_var.get()
            
            total_files = len(self.files_to_compress)
            self.compress_progress['maximum'] = total_files
            self.compress_progress['value'] = 0
            
            with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED, compresslevel=level) as zipf:
                for i, item in enumerate(self.files_to_compress):
                    self.status_bar.config(text=f"正在压缩: {os.path.basename(item)}")
                    
                    if os.path.isfile(item):
                        zipf.write(item, os.path.basename(item))
                    else:
                        self.add_folder_to_zip(zipf, item, os.path.basename(item))
                    
                    self.compress_progress['value'] = i + 1
                    self.root.update_idletasks()
                    
            self.status_bar.config(text=f"压缩完成: {output_file}")
            messagebox.showinfo("完成", "文件压缩完成")
            
        except Exception as e:
            self.status_bar.config(text=f"压缩失败: {str(e)}")
            messagebox.showerror("错误", f"压缩过程中发生错误: {str(e)}")
            
        finally:
            self.compress_running = False
            self.compress_button.config(state=tk.NORMAL)
            self.compress_progress['value'] = 0

    def add_folder_to_zip(self, zipf, folder, folder_name):
        for file in os.listdir(folder):
            full_path = os.path.join(folder, file)
            if os.path.isfile(full_path):
                zipf.write(full_path, os.path.join(folder_name, file))
            else:
                self.add_folder_to_zip(zipf, full_path, os.path.join(folder_name, file))

    def start_extraction(self):
        extract_file = self.extract_file_var.get()
        if not extract_file:
            messagebox.showerror("错误", "请选择要解压的压缩文件")
            return
            
        extract_path = self.extract_path_var.get()
        if not extract_path:
            messagebox.showerror("错误", "请选择解压目录")
            return
            
        if self.extract_running:
            messagebox.showinfo("提示", "解压任务正在进行中，请等待完成")
            return
            
        self.extract_running = True
        self.extract_button.config(state=tk.DISABLED)
        self.status_bar.config(text="解压中...")
        
        # 使用线程避免界面卡死
        threading.Thread(target=self.extract_files, daemon=True).start()

    def extract_files(self):
        try:
            extract_file = self.extract_file_var.get()
            extract_path = self.extract_path_var.get()
            
            with zipfile.ZipFile(extract_file, 'r') as zipf:
                file_list = zipf.infolist()
                total_files = len(file_list)
                self.extract_progress['maximum'] = total_files
                self.extract_progress['value'] = 0
                
                for i, file_info in enumerate(file_list):
                    filename = file_info.filename
                    self.status_bar.config(text=f"正在解压: {filename}")
                    
                    zipf.extract(file_info, extract_path)
                    
                    self.extract_progress['value'] = i + 1
                    self.root.update_idletasks()
                    
            self.status_bar.config(text=f"解压完成: {extract_file}")
            messagebox.showinfo("完成", "文件解压完成")
            
        except Exception as e:
            self.status_bar.config(text=f"解压失败: {str(e)}")
            messagebox.showerror("错误", f"解压过程中发生错误: {str(e)}")
            
        finally:
            self.extract_running = False
            self.extract_button.config(state=tk.NORMAL)
            self.extract_progress['value'] = 0

def main():
    root = tk.Tk()
    app = FileCompressorApp(root)
    root.mainloop()

if __name__ == "__main__":
    main() 